/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package graphviewer;

import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Sabitov Artem
 * @since 05.09.2013
 */
public class CalcDistance {
	/**
	 * Базовый алгоритм расчета расстояния между нодами без учета корректировки
	 * силы между связанными нодами.
	 * 
	 * Описание и принцип работы: 
	 * 
	 * Все ноды считаются последовательно, для каждой ноды составляется список
	 * доступных нод с которыми она может взаимодействовать. Далее, по-очереди,
	 * c каждой взаимодействующей нодой, для текущей ноды рассчитывается 
	 * смещение с учетом расстояния между ними.
	 * 
	 * MIN_DISTANCE - максимальное расстояние, на котором действует сила отталкивания.
	 * MAX_DISTANCE - мимальное расстояние, на котором действовует сила притяжения.
	 * (MAX_DISTANCE-MIN_DISTANCE) - отрезок, на котором силы "компенсируют" друг друга.
	 * 
	 * FORCE_IN - константа, представляющая собой коофициент(силу) притяжения.
	 * FORCE_OUT - константа, представляющая собой коофициент(силу) отталкивания.
	 * 
	 * Если расстояние больше MAX_DISTANCE:
	 * dX = (X2 - X1) / d^FROCE_IN смещение по оси Х.
	 * dY = (Y2 - Y1) / d^FROCE_IN смещение по оси Y.
	 * Если расстояние меньше MIN_DISTANCE:
	 * dX = (X2 - X1) / d^FROCE_OUT смещение по оси Х.
	 * dY = (Y2 - Y1) / d^FROCE_OUT смещение по оси Y.
	 * 
	 * Смещение за один так рассчитывается относительно всех действующих сил.
	 * Если все взаимодействующие силы компенсируют друг друга то смещение нулевое.
	 */
	
	private static final int GRAVITY_IN = 0; //при значении 0 дистанция считается меньше distance
	private static final int GRAVITY_OUT = 1; //при значении 0 дистанция считается меньше distance
	// оптимальная разница 0.3 в сторону притяжения
	public static double FORCE_IN = 2.8; //сила притяжения 2.8
	public static double FORCE_OUT = 2.5; //сила отталкивания
	public static int MIN_DISTANCE = 60;
	public static int MAX_DISTANCE = 61;
	public static int TIME_FACTOR = 1;
	
	private static double getDistance(Node n1, Node n2) {
		return Math.sqrt((double)(n2.getCoordX() - n1.getCoordX())*(n2.getCoordX() - n1.getCoordX())
				+ (n2.getCoordY() - n1.getCoordY())*(n2.getCoordY() - n1.getCoordY()));
	}
	
    public static void calculateDistance(ArrayList<Node> nodes) {
		ArrayList<Offset> offset = new ArrayList<>();
		boolean bEnd = false;
		boolean offsetAvailable = false;
			
		while(!bEnd) {
			try {
				Thread.sleep(TIME_FACTOR);
			} catch (InterruptedException ex) {
				Logger.getLogger(CalcDistance.class.getName()).log(Level.SEVERE, null, ex);
			}
//			System.out.println("\ncalculation offset...\n");
			for(int i = 0; i < nodes.size(); i++) {
				// получаем ноду которую будем смещать относительно других нод
				Node nodeToMove = nodes.get(i);
				for(int j = 0; j < nodes.size(); j++) {
					if(i != j) {
						// получаем ноду относительно которой будем рассчитывать смещение
						Node nodeFromMove = nodes.get(j);
						double distanceBetweenNodes = getDistance(nodeToMove, nodeFromMove);
//						System.out.print("node id: " + i + " (x:" + nodeToMove.getCoordX()	+ " | y:" + nodeToMove.getCoordY() + ")"
//								+ "\tnode id: " + j + " (x: " + nodeFromMove.getCoordX() + " | y:" + nodeFromMove.getCoordY() + ")"
//								+ "\t\t | distance: " + (int) distanceBetweenNodes);
						if(distanceBetweenNodes > MAX_DISTANCE) {
							// здесь рассчитываем смещение на притяжение
							// хотя бы одна нода доступна для смещения, ставим флаг на процессинг
							offsetAvailable = true;
							// рассчитываем смещение
							double offsetX;
							double offsetY;
							int offsetType; //0 - сдвигаем, 1 - раздвигаем
							offsetX =  ((nodeFromMove.getCoordX() - nodeToMove.getCoordX()) / 
									(Math.pow(distanceBetweenNodes, FORCE_IN)));// / 2));
							offsetY =  ((nodeFromMove.getCoordY() - nodeToMove.getCoordY()) / 
									(Math.pow(distanceBetweenNodes, FORCE_IN)));// / 2));
//							System.out.println("\t | offset x: " + offsetX + "\toffset y:" + offsetY);
							nodeToMove.addOffset(offsetX, offsetY);
							nodeToMove.setTypeOffset(GRAVITY_IN);
						} else if(distanceBetweenNodes < MIN_DISTANCE) {
							// здесь рассчитываем смещение на отталкивание
							// хотя бы одна нода доступна для смещения, ставим флаг на процессинг
							offsetAvailable = true;
							// рассчитываем смещение
							double offsetX;
							double offsetY;
							int offsetType; //0 - сдвигаем, 1 - раздвигаем
							offsetX =  ((nodeFromMove.getCoordX() - nodeToMove.getCoordX()) / 
									(Math.pow(distanceBetweenNodes, FORCE_OUT)));// / 2));
							offsetY =  ((nodeFromMove.getCoordY() - nodeToMove.getCoordY()) / 
									(Math.pow(distanceBetweenNodes, FORCE_OUT)));// / 2));
//							System.out.println("\t | offset x: " + offsetX + "\toffset y:" + offsetY);
							nodeToMove.addOffset(offsetX*(-1), offsetY*(-1));
							nodeToMove.setTypeOffset(GRAVITY_OUT);
						} else if(distanceBetweenNodes < MAX_DISTANCE || distanceBetweenNodes > MIN_DISTANCE) {
//							System.out.println("\t | offset is over...");
//							bEnd = true;
						} else if(distanceBetweenNodes > MIN_DISTANCE || distanceBetweenNodes < MAX_DISTANCE) {
//							System.out.println("\t | offset is over...");
//							bEnd = true;
						} 
					}
				}
//				System.out.println("\t| total offset x: " + nodeToMove.getOffsetX()
//						+ "\t| total offset y: " + nodeToMove.getOffsetY());
			}
			// нужно ли производить смещение
			if(!offsetAvailable) {
				bEnd = true;
			} else {	
//				System.out.println("\nprocessing offset...\n");
				for(int i = 0 ; i < nodes.size(); i++) {
					// выполняем смещение в текущем такте
					nodes.get(i).performOffset();
//					System.out.println("node id: " + i + " (x:" + nodes.get(i).getOldX() + " -> x:" + nodes.get(i).getCoordX()
//							+ "\t| y:" + nodes.get(i).getOldY() + " -> y:" + nodes.get(i).getCoordY() + ")");
					// обнуляем смещение для следующего такта
					nodes.get(i).resetOffset();
				}
				offsetAvailable = false;
			}
		}
    }
}
